#include <string>
#include <unordered_map>
#include <iostream>
#include <vector>
#include <memory>
enum class FRUIT
{
    APPLE,
    BANANA,
    ORANGE
};
class ServerCenter;
int id = 0;

class NotifyBase
{
private:
    int _id;
    ServerCenter *_store;

public:
    NotifyBase(int id, ServerCenter *store) : _id(id), _store(store) {}
    virtual ~NotifyBase() {}
    int Id() { return _id; }    
    void subscribe(FRUIT fruit);
    void Unsubscribe(FRUIT fruit);
    virtual void Update(FRUIT fruit) {}

};

class CustomerByEmail : public NotifyBase
{
private:
    std::string _email;

public:
    CustomerByEmail(const std::string& email,int id, ServerCenter *store):
    NotifyBase(id,store),_email(email){}
    virtual void Update(FRUIT fruit) override
    {
        std::cout << "Send to phone " << _email << " "<<(int)fruit <<" is update!"<<std::endl;
    }
};
class CustomerByMsg : public NotifyBase
{
private:
    std::string _phone;

public:
    CustomerByMsg(const std::string& phone,int id, ServerCenter *store):
    NotifyBase(id,store),_phone(phone){}
    virtual void Update(FRUIT fruit) override
    {
        std::cout << "Send to phone " << _phone << " "<<(int)fruit <<" is update!"<<std::endl;
    }
};

class ServerCenter
{
public:
private:
    class FruitInfo
    {
    public:
        FRUIT _fruit;
        int _reserve;
        int _price;
    };
    std::unordered_map<FRUIT, std::vector<NotifyBase *>> _customers;
    std::unordered_map<FRUIT, FruitInfo> _fruit_info;

public:
    void Subscribe(NotifyBase *cus, FRUIT fruit)
    {
        _customers[fruit].push_back(cus);
    }
    void UnSubscribe(NotifyBase *cus, FRUIT fruit)
    {
        auto &customers = _customers[fruit];
        for (int i = 0; i < customers.size(); ++i)
        {
            if (customers[i]->Id() == cus->Id())
            {
                customers.erase(customers.begin() + i);
                break;
            }
        }
    }
    void AddFruit(FRUIT fruit, const FruitInfo &msg)
    {
        _fruit_info.insert(std::make_pair(fruit, msg));
    }
    void RemoveFruit(FRUIT fruit)
    {
        auto iter = _fruit_info.find(fruit);
        if (iter != _fruit_info.end())
        {
            _fruit_info.erase(fruit);
        }
    }
    void NotifyAll(FRUIT fruit)
    {
        for (auto &p : _customers)
        {
            for (auto &customer : p.second)
            {
                customer->Update(fruit);
            }
        }
    }

    void Notify(FRUIT fruit)
    {
        auto &customers = _customers[fruit];
        for (auto &customer : customers)
        {
            customer->Update(fruit);
        }
    }
};
void NotifyBase::subscribe(FRUIT fruit)
{
    _store->Subscribe(this, fruit);
}
void NotifyBase::Unsubscribe(FRUIT fruit){
    _store->UnSubscribe(this, fruit);
}
// class A
// {
// public:
//     A()
//     {
//         std::cout << "A()" << std::endl;
//     }
//     ~A()
//     {
//         std::cout << "~A()" << std::endl;
//     }
// };

// class B : public A
// {
// public:
//     B()
//     {
//         std::cout << "B()" << std::endl;
//     }
//     ~B() 
//     {
//         std::cout << "~B()" << std::endl;
//     }
// };

int main()
{
    std::shared_ptr<ServerCenter> center = std::make_shared<ServerCenter>();
    std::shared_ptr<NotifyBase> c1 = std::make_shared<CustomerByEmail>("bybit@163.com",++id,center.get());
    std::shared_ptr<NotifyBase> c2 = std::make_shared<CustomerByEmail>("wangju@gmail.com",++id,center.get());
    std::shared_ptr<NotifyBase> c3 = std::make_shared<CustomerByEmail>("jackie@163.com",++id,center.get());
    std::shared_ptr<NotifyBase> c4 = std::make_shared<CustomerByMsg>("13884546343",++id,center.get());
    std::shared_ptr<NotifyBase> c5 = std::make_shared<CustomerByMsg>("15009393594",++id,center.get());
    c1->subscribe(FRUIT::APPLE);
    c1->subscribe(FRUIT::BANANA);
    c1->subscribe(FRUIT::ORANGE);
    c2->subscribe(FRUIT::APPLE);
    c2->subscribe(FRUIT::ORANGE);
    c3->subscribe(FRUIT::BANANA);
    c3->subscribe(FRUIT::ORANGE);
    c4->subscribe(FRUIT::ORANGE);
    c4->subscribe(FRUIT::BANANA);
    c5->subscribe(FRUIT::ORANGE);
    // center->Notify(FRUIT::APPLE);
    center->Notify(FRUIT::BANANA);
    return 0;
}
